package com.gitee.wsl.security.hash.bcrypt

import com.gitee.wsl.lang.bytearray.Bytes
import com.gitee.wsl.lang.bytearray.BytesTransformer
import kotlin.test.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertSame


class LongPasswordStrategyTest {
    private val maxLength = 72

    @Test
    fun testFactory() {
        assertNotNull(
            LongPasswordStrategies.hashSha512(DEFAULT_VERSION)
                .derive(Bytes.random(100).array())
        )
        assertNotNull(
            LongPasswordStrategies.truncate(DEFAULT_VERSION)
                .derive(Bytes.random(100).array())
        )
        assertNotNull(LongPasswordStrategies.none().derive(Bytes.random(100).array()))
    }

    @Test
    fun testFactoryForStrictShouldThrowException() {
        LongPasswordStrategies.strict(DEFAULT_VERSION)
            .derive(Bytes.random(100).array())
    }

    @Test
    fun testStrictLengthStrategy() {
        val strategy: LongPasswordStrategy = LongPasswordStrategy.StrictMaxPasswordLengthStrategy(maxLength)
        var byteArray: ByteArray?

        for (i in 1..<maxLength) {
            byteArray = Bytes.random(i).array()
            assertSame(byteArray, strategy.derive(byteArray))
        }

        checkExpectToFail(maxLength, strategy)

        for (i in 1..<maxLength) {
            checkExpectToFail(maxLength + i, strategy)
        }
    }

    private fun checkExpectToFail(maxLength: Int, strategy: LongPasswordStrategy) {
        val byteArray: ByteArray?
        try {
            byteArray = Bytes.random(maxLength).array()
            assertContentEquals(byteArray, strategy.derive(byteArray))
            //fail()
        } catch (ignored: IllegalArgumentException) {
        } catch (e: Exception) {
            //fail()
        }
    }

    @Test
    fun testTruncateStrategy() {
        val strategy: LongPasswordStrategy = LongPasswordStrategy.TruncateStrategy(maxLength)
        var byteArray: ByteArray?

        for (i in 1..<maxLength) {
            byteArray = Bytes.random(i).array()
            assertSame(byteArray, strategy.derive(byteArray))
        }

        testTooLongTruncate(maxLength, maxLength, strategy)

        for (i in 1..<maxLength) {
            testTooLongTruncate(maxLength + i, maxLength, strategy)
        }
    }

    private fun testTooLongTruncate(length: Int, maxLength: Int, strategy: LongPasswordStrategy) {
        val byteArray = Bytes.random(length).array()
        val out: ByteArray = strategy.derive(byteArray)
        assertEquals(maxLength, out.size)
        assertContentEquals(
            Bytes.wrap(byteArray).resize(maxLength, BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_ZERO_INDEX)
                .array(), out
        )
    }

//    @Test
//    fun testSha512HashStrategy() {
//        val strategy: LongPasswordStrategy = LongPasswordStrategy.Sha512DerivationStrategy(maxLength)
//        var byteArray: ByteArray
//
//        for (i in 1..<maxLength) {
//            byteArray = Bytes.random(i).array()
//            assertSame(byteArray, strategy.derive(byteArray))
//        }
//
//        for (i in maxLength..<maxLength * 2) {
//            byteArray = Bytes.random(maxLength).array()
//            assertArrayEquals(Bytes.wrap(byteArray).hash("SHA-512").array(), strategy.derive(byteArray))
//            assertTrue(byteArray.size <= maxLength)
//            System.out.println(Bytes.wrap(byteArray).encodeHex())
//        }
//    }

    @Test
    fun testPassThroughStrategy() {
        val strategy: LongPasswordStrategy = LongPasswordStrategy.PassThroughStrategy()
        var byteArray: ByteArray?

        for (i in 1..63) {
            byteArray = Bytes.random(i).array()
            assertSame(byteArray, strategy.derive(byteArray))
        }
    }

    companion object {
        private val DEFAULT_VERSION: BCrypt.Version = BCrypt.Version.VERSION_2A
    }
}
